Eu criei um aplicativo Tiny Journal para aprender Laravel. Aqui está o processo, passo a passo.


A maioria dos tutoriais do Laravel fornece código funcional e o orienta. Isso é bom para ver como as peças se encaixam, mas não ensina muito sobre o que acontece quando você deixa um campo em branco, digita o nome errado do parâmetro ou hesita antes de executar um comando no qual ainda não confia totalmente.

Então, em vez de seguir um tutorial, criei algo pequeno do zero: o Tiny Journal, um aplicativo de registro no diário de uma única página. Escreva uma entrada, leia-a, edite-a, exclua-a. Sem autenticação, sem tags, sem pesquisa. Pequeno o suficiente para terminar em um fim de semana, grande o suficiente para atingir conceitos reais do Laravel ao longo do caminho.

Esse é o processo que segui, na ordem, inclusive as peças que quebraram.

O que você precisa

  • Conhecimento básico de PHP (variáveis, funções, arrays)
  • Uma instalação local do Laravel
  • Familiaridade com MVC em nível conceitual, você não precisa de experiência específica em Laravel

Etapa 1: o modelo e a migração

Tudo começa com um modelo, Entrye uma migração:

php artisan make:model Entry -m
Schema::create('entries', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('content');
    $table->timestamps();
});
php artisan migrate

Dois campos, title e contentambos obrigatórios no nível do banco de dados. Esse último detalhe importa mais do que deveria, é a razão pela qual o primeiro bug real neste projeto existiu.

Etapa 2: leitura de entradas (index e show)

Com a tabela instalada, o primeiro recurso funcional era apenas exibir entradas:

public function index()
{
    $entries = Entry::all();
    return view('entries.index', ('entries' => $entries));
}

public function show(Entry $entry)
{
    return view('entries.show', ('entry' => $entry));
}

show() já usa vinculação de modelo de rota aqui, o Laravel corresponde ao {entry} espaço reservado na rota para o Entry $entry parâmetro e busca o registro automaticamente, sem manual find() necessário. Só percebi isso totalmente mais tarde, quando tentei escrever edit() percorri o longo caminho e percebi a incompatibilidade (mais sobre isso abaixo).

Etapa 3: Criação de entradas (create e store)

public function create()
{
    return view('entries.create');
}

public function store(Request $request)
{
    $entry = new Entry();
    $entry->title = $request->title;
    $entry->content = $request->content;
    $entry->save();

    return redirect('/');
}

Isso funcionou até eu enviar o formulário com o título deixado em branco. O que voltou foi um erro de banco de dados de página inteira, uma violação de restrição NOT NULL, direto da migração na Etapa 1. O banco de dados estava aplicando corretamente uma regra que eu mesmo escrevi, apenas no pior lugar possível, após o formulário já ter sido enviado, com um rastreamento de pilha em vez de uma frase na qual o usuário poderia agir.

A correção é a validação, chamada antes que qualquer coisa toque o banco de dados:

public function store(Request $request)
{
    $request->validate((
        'title' => 'required',
        'content' => 'required',
    ));

    $entry = new Entry();
    $entry->title = $request->title;
    $entry->content = $request->content;
    $entry->save();

    return redirect('/');
}

validate() permite a solicitação ou a interrompe e redireciona de volta para o formulário automaticamente. Para realmente exibir o erro, faça um loop no saco de erros do Laravel na visualização:

@foreach ($errors->all() as $error)
    

{{ $error }}

@endforeach

Uma pegadinha: $errors não é um array simples que você pode percorrer diretamente, é um MessageBage você precisa ->all() para extrair as sequências de mensagens. Loop $errors em si e você não obtém nada, nenhum erro, nenhuma falha, apenas silêncio.

Etapa 4: Editando entradas (edit e update)

public function edit($id)
{
    $entry = Entry::find($id);
    return view('entries.edit', ('entry' => $entry));
}

public function update(Request $request, Entry $entry)
{
    $entry->title = $request->input('title');
    $entry->content = $request->input('content');
    $entry->save();

    return redirect('/');
}

Duas coisas surgiram aqui. Primeiro, a mesma falha do título em branco da Etapa 3 ainda era possível update()pois ainda não tinha validação. A correção é idêntica, apenas sequenciada corretamente, valide antes de escrever qualquer coisa $entrynão depois:

public function update(Request $request, Entry $entry)
{
    $request->validate((
        'title' => 'required',
        'content' => 'required',
    ));

    $entry->title = $request->input('title');
    $entry->content = $request->input('content');
    $entry->save();

    return redirect('/');
}

Em segundo lugar, comparando edit() e update() clique lado a lado para vincular o modelo de rota. update() usa Entry $entry e nunca liga find(). edit() ainda usei o manual $id + Entry::find($id) padrão. Uma vez que eu reescrevi edit() para combinar:

public function edit(Entry $entry)
{
    return view('entries.edit', ('entry' => $entry));
}

A regra tornou-se óbvia: a vinculação do modelo de rota funciona sempre que a URL contém um ID apontando para algo que já existe. Isso é verdade para edit, updatee destroy. Nunca é verdade para store ou indexnão há nenhum registro específico nesses URLs para vincular em primeiro lugar.

Etapa 5: um bug mais sutil, entrada antiga

Depois de adicionar a validação, algo parecia certo pelo motivo errado. Digite um título real, deixe o conteúdo em branco e envie. A página é recarregada e o campo de título ainda mostra o que digitei. Parecia uma entrada antiga funcionando. Não foi, o título era apenas $entry->titleintocado, porque a validação interrompeu o salvamento antes que algo chegasse ao banco de dados.

A correção real é o old() ajudante:

title) }}">

old('title', $entry->title) verifica primeiro as entradas restantes de um envio com falha. Se existir, ele vence. Se a página for aberta do zero, ela retornará ao valor armazenado da entrada. O verdadeiro teste: mudar o título, apagar o conteúdo, enviar. Se estiver conectado corretamente, o título editado sobreviverá à viagem de ida e volta em vez de ser revertido.

Etapa 6: excluir entradas e reunir seis rotas em uma

public function destroy(Entry $entry)
{
    $entry->delete();
    return redirect('/');
}

A essa altura, eu tinha seis rotas escritas à mão, uma por ação:

Route::get('/entries', (EntryController::class, 'index'));
Route::get('/entries/create', (EntryController::class, 'create'));
Route::post('/entries', (EntryController::class, 'store'));
Route::get('/entries/{entry}/edit', (EntryController::class, 'edit'));
Route::put('/entries/{entry}', (EntryController::class, 'update'));
Route::delete('/entries/{entry}', (EntryController::class, 'destroy'));

Laravel recolhe todos os seis em uma linha:

Route::resource('entries', EntryController::class);

Fiquei pensando nisso por mais tempo do que deveria, convencido de que a mudança forçaria mudanças em outros lugares. Não aconteceu. O controlador não mudou nada, apenas o arquivo de rotas mudou. Route::resource() não está gerando um novo comportamento, são as mesmas seis rotas, os mesmos nomes, os mesmos verbos, pré-montados.

Etapa 7: Limpeza com layout compartilhado

Quando tudo funcionou, todas as quatro visualizações (index, create, edit, show) tinham seu próprio completo , e “ tags, copiadas e coladas quatro vezes. Um layout compartilhado corrige isso:

{{-- resources/views/layouts/app.blade.php --}}



    Tiny Journal
    


    @yield('content')


Cada visualização é reduzida apenas ao seu conteúdo exclusivo:

{{-- resources/views/entries/index.blade.php --}}
@extends('layouts.app')

@section('content')
    
    @foreach ($entries as $entry)
        

{{ $entry->title }}

{{ $entry->content }}

id }}">View id }}/edit">Edit @endforeach @endsection

Adicione um link de folha de estilo ao layout uma vez e cada página o seleciona automaticamente, nenhum outro arquivo precisa ser tocado.

As Paredes, Resumidamente

Olhando para trás, para o processo acima, os pontos de atrito reais foram:

  1. Um título em branco travando o aplicativo (Etapa 3), o banco de dados aplicando uma regra na camada errada.
  2. **O mesmo acidente escondido em **update() (Etapa 4), a mesma correção, necessitando apenas do sequenciamento correto.
  3. Clique na vinculação do modelo de rota (Etapa 4), uma vez que comparei um método que o usava com outro que não o usava.
  4. Entrada antiga parecendo que funcionou antes de realmente funcionar (Etapa 5), ​​dois mecanismos diferentes que produziram resultados de aparência idêntica.
  5. **Hesitando antes **Route::resource() (Passo 6), medo de uma mudança que se revelou segura.
  6. Quatro shells de páginas duplicados (Etapa 7), corrigido quando a repetição se tornou irritante o suficiente para ser notada.

Nada disso foi individualmente difícil. O que foi difícil foi perceber, naquele momento, que um bug ou uma hesitação apontava para um conceito faltante, não para uma linha de código faltante.

Para onde vai o Tiny Journal a partir daqui

Algumas próximas etapas óbvias se você estiver avançando:

  • Mensagens instantâneas após criar, atualizar e excluir, para que o usuário receba confirmação em vez de um redirecionamento silencioso
  • Regras de validação mais fortescomprimentos mínimos, limites de caracteres, em vez de apenas required
  • Exclusões suavesentão “excluir” não significa desaparecer permanentemente

Se você está aprendendo Laravel agora: construa algo pequeno, quebre-o de propósito e leia o erro em vez de colá-lo imediatamente em uma barra de pesquisa. A estrutura geralmente já tem uma resposta para qualquer repetição ou fragilidade que você esteja sentindo. Você só precisa bater na parede primeiro para saber qual pergunta fazer.



Source link